package com.netease.nimlib.ipc.cp.provider;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

import com.netease.nimlib.ipc.cp.data.IDataHandler;
import com.netease.nimlib.ipc.cp.protocol.Protocol;

import java.util.HashMap;
import java.util.Map;

/**
 * 进程间数据同步Provider基类
 * <p>
 * Created by huangjun on 2017/10/31.
 **/

public abstract class AbsContentProvider extends ContentProvider {

    private Map<String, IDataHandler> dataHandlerMap = new HashMap<>();

    public abstract IDataHandler onGetDataHandler(Context context, final String name);

    /**
     * ********************************* ContentProvider impl **************************************
     */

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        MatrixCursor cursor = null;
        Protocol.KeyInfo k = getKeyInfoFromUri(uri);
        switch (Protocol.matchUri(uri)) {
            case Protocol.URI_TYPE_BOOLEAN:
                if (getDataHandler(k.getName()).hasKey(k.getKey())) {
                    cursor = toCursor(getDataHandler(k.getName()).onQueryBoolean(k.getKey(), false) ? 1 : 0);
                }
                break;
            case Protocol.URI_TYPE_STRING:
                if (getDataHandler(k.getName()).hasKey(k.getKey())) {
                    cursor = toCursor(getDataHandler(k.getName()).onQueryString(k.getKey(), ""));
                }
                break;
            case Protocol.URI_TYPE_INT:
                if (getDataHandler(k.getName()).hasKey(k.getKey())) {
                    cursor = toCursor(getDataHandler(k.getName()).onQueryInt(k.getKey(), 0));
                }
                break;
            case Protocol.URI_TYPE_LONG:
                if (getDataHandler(k.getName()).hasKey(k.getKey())) {
                    cursor = toCursor(getDataHandler(k.getName()).onQueryLong(k.getKey(), 0L));
                }
                break;
            case Protocol.URI_TYPE_FLOAT:
                if (getDataHandler(k.getName()).hasKey(k.getKey())) {
                    cursor = toCursor(getDataHandler(k.getName()).onQueryFloat(k.getKey(), 0.0f));
                }
                break;
        }
        return cursor;
    }

    private <T> MatrixCursor toCursor(T value) {
        MatrixCursor matrixCursor = new MatrixCursor(Protocol.CURSOR_COLUMN_NAMES, 1);
        MatrixCursor.RowBuilder builder = matrixCursor.newRow();
        builder.add(value);
        return matrixCursor;
    }

    private Protocol.KeyInfo getKeyInfoFromUri(Uri uri) {
        if (uri == null || uri.getPathSegments().size() != 3) {
            throw new IllegalArgumentException("getKeyInfoFromUri uri is wrong : " + uri);
        }

        String name = uri.getPathSegments().get(1);
        String key = uri.getPathSegments().get(2);
        return new Protocol.KeyInfo(name, key);
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        Protocol.KeyInfo k = getKeyInfoFromUri(uri);

        switch (Protocol.matchUri(uri)) {
            case Protocol.URI_TYPE_STRING:
                handleString(uri, k.getName(), values);
                break;
            case Protocol.URI_TYPE_BOOLEAN:
                handleBoolean(uri, k.getName(), values);
                break;
            case Protocol.URI_TYPE_LONG:
                handleLong(uri, k.getName(), values);
                break;
            case Protocol.URI_TYPE_INT:
                handleInt(uri, k.getName(), values);
                break;
            case Protocol.URI_TYPE_FLOAT:
                handleFloat(uri, k.getName(), values);
                break;
            case Protocol.URI_TYPE_VOID:
                handleVoid(k.getName(), values);
                break;
            default:
                throw new IllegalStateException("update error, as unsupported uri : " + uri);
        }
        return 0;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        switch (Protocol.matchUri(uri)) {
            case Protocol.URI_TYPE_BOOLEAN:
            case Protocol.URI_TYPE_LONG:
            case Protocol.URI_TYPE_STRING:
            case Protocol.URI_TYPE_INT:
            case Protocol.URI_TYPE_FLOAT:
                Protocol.KeyInfo k = getKeyInfoFromUri(uri);
                getDataHandler(k.getName()).onRemoveKey(k.getKey());
                break;
            default:
                throw new IllegalStateException("unsupported uri : " + uri);
        }
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        throw new IllegalStateException("insert unsupported!!!");
    }

    @Override
    public boolean onCreate() {
        return true;
    }

    /**
     * ********************************* handle data **************************************
     */

    private void handleString(final Uri uri, final String name, final ContentValues values) {
        if (values == null) {
            throw new IllegalArgumentException(" values is null!!!");
        }

        String k = values.getAsString(Protocol.URI_KEY);
        String v = values.getAsString(Protocol.URI_VALUE);
        getDataHandler(name).onHandleString(k, v);

        Boolean notify = values.getAsBoolean(Protocol.URI_NOTIFY);
        if (notify != null && notify) {
            notifyObservers(uri);
        }
    }

    private void handleBoolean(final Uri uri, final String name, final ContentValues values) {
        if (values == null) {
            throw new IllegalArgumentException(" values is null!!!");
        }

        String k = values.getAsString(Protocol.URI_KEY);
        boolean v = values.getAsBoolean(Protocol.URI_VALUE);
        getDataHandler(name).onHandleBoolean(k, v);

        Boolean notify = values.getAsBoolean(Protocol.URI_NOTIFY);
        if (notify != null && notify) {
            notifyObservers(uri);
        }
    }

    private void handleInt(final Uri uri, final String name, final ContentValues values) {
        if (values == null) {
            throw new IllegalArgumentException(" values is null!!!");
        }

        String k = values.getAsString(Protocol.URI_KEY);
        int v = values.getAsInteger(Protocol.URI_VALUE);
        getDataHandler(name).onHandleInt(k, v);

        Boolean notify = values.getAsBoolean(Protocol.URI_NOTIFY);
        if (notify != null && notify) {
            notifyObservers(uri);
        }
    }

    private void handleLong(final Uri uri, final String name, final ContentValues values) {
        if (values == null) {
            throw new IllegalArgumentException(" values is null!!!");
        }

        String k = values.getAsString(Protocol.URI_KEY);
        long v = values.getAsLong(Protocol.URI_VALUE);
        getDataHandler(name).onHandleLong(k, v);

        Boolean notify = values.getAsBoolean(Protocol.URI_NOTIFY);
        if (notify != null && notify) {
            notifyObservers(uri);
        }
    }

    private void handleFloat(final Uri uri, final String name, final ContentValues values) {
        if (values == null) {
            throw new IllegalArgumentException(" values is null!!!");
        }

        String k = values.getAsString(Protocol.URI_KEY);
        float v = values.getAsFloat(Protocol.URI_VALUE);
        getDataHandler(name).onHandleFloat(k, v);

        Boolean notify = values.getAsBoolean(Protocol.URI_NOTIFY);
        if (notify != null && notify) {
            notifyObservers(uri);
        }
    }

    private void handleVoid(String name, ContentValues values) {
        if (values == null) {
            throw new IllegalArgumentException(" values is null!!!");
        }

        String k = values.getAsString(Protocol.URI_KEY);
        String v = values.getAsString(Protocol.URI_VALUE);
        getDataHandler(name).onHandleVoid(k, v);
    }

    private void notifyObservers(final Uri uri) {
        if (getContext() == null) {
            return;
        }

        Log.i(Protocol.TAG, "notify change, uri=" + uri);
        getContext().getContentResolver().notifyChange(uri, null);
    }

    protected IDataHandler getDataHandler(final String name) {
        if (TextUtils.isEmpty(name)) {
            throw new IllegalArgumentException("getDataHandler name is null!!!");
        }

        if (dataHandlerMap.get(name) == null) {
            IDataHandler pref = onGetDataHandler(getContext(), name);
            dataHandlerMap.put(name, pref);
        }

        return dataHandlerMap.get(name);
    }
}
